/******************************************************************
 * Created by: GOh Chee Hong                                      
 * Date of Creation: 1/12/2008
 * Date of last modification: 1/3/2009
 * Status: Optimized and Finished
 *                                                                
 * Description: uplodaing message and file to an inbox in OpenLookUp
 ******************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Threading;
using Pbdms.Common.CryptoUtils;
using Pbdms.Common.SharedVariables;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Bcpg;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace Pbdms.BusinessLogic.Mail
{
    public class MailUploaderManager
    {
        /// <summary>
        /// delegate that handle the status of the document
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public delegate void StatusHandler(object sender, string args);
        public StatusHandler statusProcess;
        /// <summary>
        /// delegate that handle the current user display
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public delegate void CurrentUserHandler(object sender, string args);
        public CurrentUserHandler CurrentUser;
        /// <summary>
        /// delegate that handle publish done; 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public delegate void PublishDoneHandler(object sender, string args);
        public PublishDoneHandler PublishDone;
        /// <summary>
        /// delegate that handle transfer of each user to the finished upload to
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public delegate void EachPublishDoneHandler(object sender, string args);
        public EachPublishDoneHandler EachPublishDone;
       
        private SecretKeyControl secretKeyControl;
        private PublicKeysControl publicKeyControl;
        private Hashtable friends;
        //set friends
        private ArrayList attachments;
        private Boolean cancel = false;
        private Message message;
        //store temp locations of the files
        private ArrayList tempFiles = new ArrayList();
        public Hashtable Friends
        {
            get { return friends; }
            set { friends = value; }
        }
        public ArrayList Attachments
        {
            get { return attachments; }
            set { attachments = value; }
        }
        public Message Message
        {
            get { return message; }
            set { message = value; }
        }
        public SecretKeyControl SecKeyControl
        {
            get { return secretKeyControl; }
            set { secretKeyControl = value; }
        }
        public PublicKeysControl PublicKeyControl
        {
            get { return publicKeyControl; }
            set { publicKeyControl = value; }
        }
        public void Cancel()
        {
            cancel = true;
        }
        public Boolean isCancel()
        {
            return cancel;
        }
        //set keycontrol
        public void UploadingMessage()
        {
            MemoryStream outputMemory = new MemoryStream();
            try
            {


                foreach (string key in friends.Keys)
                {
                    Boolean isUploaded = false;
                    if (CurrentUser != null)
                    {
                        CurrentUser(this, (string)friends[key]);
                    }


                    //assigning key to message  
                    //key format:Uploaded On:date Created by:yourpublickkeyid Sent To friend'spublickey 
                    string messageKey = "Uploaded On:" + message.datetime.ToString("r") + " Created By:" + secretKeyControl.readSecretKey().KeyId.ToString("X") + " Sent To:" + key;
                    Message.key = messageKey;



                    //assigning keys to files
                    Message.attachments.Clear();
                    int i = 0;
                    //signal prepare attachments
                    if (attachments.Count != 0)
                    {
                        if (statusProcess != null)
                        {
                            statusProcess(this, "Prepare attachments");
                            Thread.Sleep(1000);
                        }
                    
                        
                    }
                    i=0;
                    PgpPublicKey friendPublicKey = publicKeyControl.getPublicKey(key);
                    Hashtable filesTobeUploaded = new Hashtable();
                    foreach (string attachmentPath in attachments)
                    {
                        
                        //get buddy's public key
                        
                        string SignedFile="";
                        string EncryptedFile="";
                        Encrypter encrypter = new Encrypter();
                        Signer signer = new Signer();
                        
                        try
                        {
                            //encrypt the file (file out, file in)
                            EncryptedFile=Common.Global.getTempFolder() + "\\tempEncryptFile" + i;
                            encrypter.EncryptFile(EncryptedFile, attachmentPath, friendPublicKey, true);
                            
                            //save temp files informations to delete after uploading
                            tempFiles.Add(Common.Global.getTempFolder() + "\\tempEncryptFile" + i);
                            
                            //sign the file (file out, file in) //compress
                            SignedFile = Common.Global.getTempFolder() + "\\tempSignEncryptFile" + i; 
                            signer.SignFile(SignedFile, EncryptedFile, secretKeyControl.readSecretKey(), secretKeyControl.readPrivateKey(), true);
                            
                            //save temp files informations to delete after uploading
                            tempFiles.Add(Common.Global.getTempFolder() + "\\tempSignEncryptFile" + i);
                           
                        }
                        catch
                        {
                            if (statusProcess != null)
                            {
                                statusProcess(this, "Error occured while encrypting and signing the attachments");
                            }
                            return;
                        }
                        //files information
                        System.IO.FileInfo fileInfo = new System.IO.FileInfo(SignedFile);
                        System.IO.FileInfo originalFileInfo = new System.IO.FileInfo(attachmentPath);
                        Common.DataObject.OfflineFile offlineFile = new Pbdms.Common.DataObject.OfflineFile();
                        //original file name
                        offlineFile.filename = originalFileInfo.Name;
                        //insert the signed file length
                        offlineFile.size = fileInfo.Length;
                        //key format equal  Uploaded On:date Created by:yourpublickkeyid Sent To friend'spublickey FileName:offlineFile.filename Attachment No:no
                        offlineFile.key = messageKey + " Attachment No:" + i.ToString();
                        i++;
                        Message.attachments.Add(offlineFile);
                        //save key and the path of the file to be uploaded
                        //save original file name and signed file path
                        Hashtable twoFiles = new Hashtable();
                        twoFiles.Add("original", originalFileInfo.Name);
                        twoFiles.Add("signed",SignedFile );
                        filesTobeUploaded.Add(offlineFile.key,twoFiles);
                    }




                    //prepare message
                   
                    
                    




                    if (filesTobeUploaded.Count != 0)
                    {

                        //push file
                        FileUploaderManager UploadManager = new FileUploaderManager(filesTobeUploaded);
                        UploadManager.statusProcess += new FileUploaderManager.StatusHandler(statusProcess);
                        UploadManager.isCancel += new FileUploaderManager.checkCancel(isCancel);
                        isUploaded = UploadManager.uploadFiles();
                        if (isUploaded == false)
                        {
                            if (statusProcess != null)
                            {
                                statusProcess(this, "Error occured while uploading files. Uploading operation aborted");
                            }
                            return;
                        }
                    }

                    if (statusProcess != null)
                    {
                        statusProcess(this, "Uploading message now");
                    }
                    Thread.Sleep(1000);
                    //publish the inbox message and pure message



                    try
                    {
                        //MemoryStream memoryStorage = new MemoryStream();
                        BinaryFormatter formatter = new BinaryFormatter();
                        formatter.Serialize(outputMemory, message);

                        outputMemory.Seek(0, SeekOrigin.Begin);
                        //encrypt the object
                        Encrypter encry = new Encrypter();

                        outputMemory = encry.EncryptBytes(outputMemory, friendPublicKey, true);
                        //sign the object
                        Signer signer = new Signer();
                        outputMemory = signer.SignMemory(outputMemory, secretKeyControl.readSecretKey(), secretKeyControl.readPrivateKey());
                    }
                    catch
                    {
                        if (statusProcess != null)
                        {
                            statusProcess(this, "Error occured while encrypting and signing the message");
                        }
                        return;
                    }

                    Mail.MessageUploader messageUploader = new MessageUploader( key, message.key,outputMemory,message.secret);
                    messageUploader.statusProcess += new MessageUploader.StatusHandler(statusProcess);
                    messageUploader.isCancel += new MessageUploader.checkCancel(isCancel);
                    isUploaded = messageUploader.PublishMessage();
                    if (isUploaded == false)
                    {
                        if (statusProcess != null)
                        {
                            statusProcess(this, "Error occured while uploading. Uploading operation aborted");
                        }
                        return;
                    }


                    //done upload meesage
                    if (statusProcess != null)
                    {
                        statusProcess(this, "Finished Uploaded message");
                    }
                    Thread.Sleep(1000);

                   


                    if (EachPublishDone != null)
                    {
                        EachPublishDone(this, (string)friends[key]);
                    }


                    //uploading the attachments



                }
            }
            catch
            {

            }
            finally
            {
                //delete files
                if (tempFiles.Count != 0)
                {
                    foreach (string fileName in tempFiles)
                    {
                        File.Delete(fileName);
                    }
                }
                if (outputMemory != null)
                {
                    outputMemory.Close();
                }
                //signal uploading is done
                if (PublishDone != null)
                {
                    PublishDone(this, null);
                }
                Thread.Sleep(1000);
            }
        }
    }
}
